# Copyright (C) 2002-2003, 2005-2006, 2009 The Written Word, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# $Id: zip.py 748 2010-11-02 11:25:38Z gary $

import binascii
import os
import select
import string
import sys
import zipfile
import zlib

import misclib
import prog

from prog.archive import Archive

# constants
unzip_prog = '%UNZIP%'

class Archive (Archive):
  # test if member exists in archive
  def exists (self, member):
    try:
      zip = zipfile.ZipFile (self.path, 'r')
    except zipfile.BadZipfile:
      raise prog.ProgArchiveError ('Errors encountered while \
opening ' + os.path.normpath (self.path) + '. It is not a valid \
zip archive.')

    files = zip.namelist ()
    zip.close ()
    if not member in files:
      return False
    return True

  # extract file from archive
  def extract (self, member, dest = None):
    if not self.exists (member):
      raise prog.ProgArchiveMemberDoesNotExist ('archive member "' + \
                                                member + '" does not \
exist in archive file ' + os.path.normpath (self.path) + '.')

    if dest:
      # create parent directory components
      if os.path.dirname (dest) and \
      not os.path.exists (os.path.dirname (dest)):
        os.makedirs (os.path.dirname (dest), 0755)

      fh = open (dest, 'w')
    else:
      fh = open (member, 'w')

    pipe1_r, pipe1_w = os.pipe ()
    pipe1 = os.fork ()
    if pipe1 == 0:
      os.close (pipe1_r)
      os.dup2 (pipe1_w, 1)
      os.close (pipe1_w)

      os.execvp (unzip_prog, [unzip_prog, '-q', '-p', self.path, member])

    os.close (pipe1_w)
    io = select.poll ()
    io.register (pipe1_r, select.POLLIN)

    _data_to_read = True
    while _data_to_read:
      events = io.poll (0)
      for event in events:
        if event[1] & select.POLLIN:
          try:
            _data = os.read (pipe1_r, 1024)
          except (IOError, OSError), msg:
            io.unregister (pipe1_r)
            os.close (pipe1_r)
            fh.close ()

            raise prog.ProgExtractError (str (msg) + ' encountered \
while extracting ' + member + ' from ' + os.path.normpath (self.path))

          if _data:
            fh.write (_data)
          else:
            io.unregister (pipe1_r)
            os.close (pipe1_r)
            _data_to_read = False

        if event[1] == select.POLLHUP:
          io.unregister (pipe1_r)
          os.close (pipe1_r)
          _data_to_read = False

    fh.close ()

  # extract files from archive matching pattern
  def unpack (self, dry_run, verbose, callback, dest = None,
              filelist = [], list_files = True):
    if not dry_run:
      self.verify_md5_sig ()

    _unzip_flags = []
    _unzip_flags_string = ''
    if not list_files:
      _unzip_flags = ['-q']
      _unzip_flags_string += ' -q'
    if dest:
      _unzip_flags += ['-d', dest]
      _unzip_flags_string += ' -d ' + dest

    _filelist_string  = ''
    if filelist:
      _filelist_string = ' ' + string.join (filelist, ' ')

    if verbose:
      callback ('msg', '$ ' + unzip_prog + _unzip_flags_string + ' -o ' +
                os.path.normpath (self.path) + _filelist_string + '\n', 2)
    if not dry_run:
      if os.spawnve (os.P_WAIT, unzip_prog, [unzip_prog] +
                                             _unzip_flags +
                                             ['-o', self.path] + filelist,
                     self.environ):
        raise prog.ProgArchiveError ('error unzip to unpack source')

  def open (self, member, mode):
    sbdb_fh, sbdb_filename = misclib.mkstempfile ()
    sbdb_fh.close ()

    self.extract (member, sbdb_filename)
    fh = open (sbdb_filename, mode)
    os.remove (sbdb_filename)

    return fh
